#ifndef __CU_PSO_H__
#define __CU_PSO_H__

#include <stdlib.h>

#include <cuda_runtime.h>

// This controls the ratio between movement and inertia
#ifndef MOVE_RAT
#define MOVE_RAT .9f
#endif

// This controls the number of swarms used
#ifndef N_SWARMS
#define N_SWARMS 8
#endif

// This controls the rate at which particles are exchanged
#ifndef CROSS_RATE
#define CROSS_RATE .01f
#endif

// This is the number of dimensions contained in the error space
#ifndef DIM
#define DIM 3
#endif

// This is the number or particles contained in each swarm
#ifndef N
#define N 32
#endif

// This defines the maximum random startup value for the parameters of particles
#ifndef MAX_VAL
#define MAX_VAL 100
#endif

// This controls the weight for direction of the swarm best and the individual
// particle respectively.
#ifndef BEST_WEIGHT
#define BEST_WEIGHT .2f
#endif
#ifndef IND_WEIGHT
#define IND_WEIGHT .1f
#endif

// This controls the tolerance for completing running
#ifndef TOL
#define TOL 12
#endif

#define frand(x) (((double)rand())/RAND_MAX)

// This controls the type of error space searching
// either double, float, or int.
#define TYPE_FLOAT
#ifdef TYPE_FLOAT
typedef float pso_type;
#else
#ifdef TYPE_DOUBLE
typedef double pso_type;
#else
typedef int pso_type;
#endif
#endif

// This structure is what holds the data for each particle in the swarm
typedef struct {
	pso_type location[DIM];
	float fitness;
	pso_type best_location[DIM];
	float best_fitness;
	pso_type inertia[DIM];
} pso_part;

// This structure contains a swarm and all bookkeeping related to the swarm
typedef struct {
	pso_part part_swarm[N];
	pso_part *best;
} pso_swarm;

// This function determines the fitness of the individual
__device__ void calc_fitness(pso_part *particle);

// This returns true if all particles are in the same location
__host__ int check_done(pso_swarm *swarms);

// This finds the center of mass location weighted for fitness
__host__ int fit_com(pso_type *com, pso_swarm *swarms);

// This returns true if the particles are considered close to each other
__host__ int part_close(pso_part *part1, pso_part *part2);

// This function moves an individual by a given amount
__device__ void move_particle(pso_part *particle, pso_type move[DIM]);

// This function caculates required move for a particle and move it
__device__ void calc_move_particle(pso_part *particle, pso_part *best, unsigned int *rand_seed);

// This function checks whether an update is required for the given particle
// and updates if needed
__device__ void check_best(pso_part *new_part, pso_part **best); 

// This function will perform a swap between to swarms, only called when needed
__global__ void swap_part(pso_swarm *swarm1, pso_swarm *swarm2, unsigned int *rand_seed);

// This function initializes a particle with random startup values between 
// -MAX_VAL and MAX_VAL
__device__ void init_part(pso_part *particle, unsigned int *seed);

__global__ void run_iterations(pso_swarm *swarm, unsigned int *seeds, unsigned int iterations);

// This function will initialize and entire swarm
__global__ void init_swarm(pso_swarm *swarm, unsigned int *seed);

__device__ void copy_part(pso_part *dest, pso_part *src);

#endif //__CU_PSO_H__
